package cn.edu.neu.simpleppt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Vector;

/**
 * 读写文件的类，使用 fastjson 来将元素转换成 json 字符串
 * 文件格式如下：
 * [
 *   [  ...第一页
 *     { ...第一页的第一个元素 },
 *     { ...第二个元素 },
 *     ...
 *   ],
 *   [
 *     ...第二页
 *   ], [...], ...
 * ]
 */
public class PPTFileIO {
    /**
     * 要存储的属性
     */
    private static final String LAYOUT_X = "layoutX";
    private static final String LAYOUT_Y = "layoutY";
    private static final String ROTATE = "rotate";
    private static final String TRANSLATE_X = "translateX";
    private static final String TRANSLATE_Y = "translateY";

    private static final String FG_COLOR = "fgColor";
    private static final String BG_COLOR = "bgColor";
    private static final String LINE_WIDTH = "lineWidth";
    private static final String LINE_GAPS = "lineGaps";

    private static final String CENTER_X = "centerX";
    private static final String CENTER_Y = "centerY";
    private static final String RADIUS_X = "radiusX";
    private static final String RADIUS_Y = "radiusY";

    private static final String WIDTH = "width";
    private static final String HEIGHT = "height";

    private static final String END_X = "endX";
    private static final String END_Y = "endY";

    private static final String POINTS = "points";

    private static final String TEXT = "text";
    private static final String TEXT_COLOR = "textColor";
    private static final String TEXT_SIZE = "textSize";
    private static final String TEXT_ALIGNMENT = "textAlignment";
    private static final String ALIGN_LEFT = "left";
    private static final String ALIGN_CENTER = "center";
    private static final String ALIGN_RIGHT = "right";

    private static final String TYPE = "type";
    private static final String RECTANGLE = "rectangle";
    private static final String ELLIPSE = "Ellipse";
    private static final String LABEL = "label";
    private static final String LINE = "line";
    private static final String POLYLINE = "polyline";

    /**
     * 写入文件时，将界面上的元素转换成 JSONObject 对象
     * @param node 要存储的元素
     * @return  返回的 JSONObject 对象
     */
    private static JSONObject nodeToJSON(Node node) {
        JSONObject obj = new JSONObject();
        obj.put(LAYOUT_X, node.getLayoutX());
        obj.put(LAYOUT_Y, node.getLayoutY());
        obj.put(TRANSLATE_X, node.getTranslateX());
        obj.put(TRANSLATE_Y, node.getTranslateY());
        obj.put(ROTATE, node.getRotate());

        if (node instanceof Shape) {
            Shape shape = (Shape) node;
            ObservableList<Double> dash = shape.getStrokeDashArray();
            obj.put(FG_COLOR, shape.getStroke().toString());
            obj.put(BG_COLOR, shape.getFill().toString());
            obj.put(LINE_WIDTH, shape.getStrokeWidth());
            obj.put(LINE_GAPS, (dash.size() > 0) ? dash.get(0) : 0f);
            if (shape instanceof Ellipse) {
                Ellipse ellipse = (Ellipse) shape;
                obj.put(TYPE, ELLIPSE);
                obj.put(CENTER_X, ellipse.getCenterX());
                obj.put(CENTER_Y, ellipse.getCenterY());
                obj.put(RADIUS_X, ellipse.getRadiusX());
                obj.put(RADIUS_Y, ellipse.getRadiusY());
            }
            if (shape instanceof Rectangle) {
                Rectangle rectangle = (Rectangle) shape;
                obj.put(TYPE, RECTANGLE);
                obj.put(WIDTH, rectangle.getWidth());
                obj.put(HEIGHT, rectangle.getHeight());
            }
            if (shape instanceof Line) {
                Line line = (Line) shape;
                obj.put(TYPE, LINE);
                obj.put(END_X, line.getEndX());
                obj.put(END_Y, line.getEndY());
            }
            if (shape instanceof Polyline) {
                Polyline polyline = (Polyline) shape;
                JSONArray points = new JSONArray();
                points.addAll(polyline.getPoints());
                obj.put(TYPE, POLYLINE);
                obj.put(POINTS, points);
            }
        } else if (node instanceof Label) {
            Label label = (Label) node;
            obj.put(TYPE, LABEL);
            obj.put(TEXT, label.getText());
            obj.put(TEXT_COLOR, label.getTextFill().toString());
            obj.put(TEXT_SIZE, label.getFont().getSize());
            obj.put(WIDTH, label.getPrefWidth());
            obj.put(HEIGHT, label.getPrefHeight());
            String align;
            if (label.getTextAlignment() == TextAlignment.LEFT) {
                align = ALIGN_LEFT;
            } else if (label.getTextAlignment() == TextAlignment.CENTER) {
                align = ALIGN_CENTER;
            } else {
                align = ALIGN_RIGHT;
            }
            obj.put(TEXT_ALIGNMENT, align);
        }

        return obj;
    }

    /**
     * 读取文件时，从读取的 JSONObject 对象设置元素属性
     * @param node 要添加到界面的元素
     * @param obj 从文本文件转换来的 JSONObject 对象
     */
    static private void setNodeInfo(Node node, JSONObject obj) {
        node.setLayoutX(obj.getDouble(LAYOUT_X));
        node.setLayoutY(obj.getDouble(LAYOUT_Y));
        node.setTranslateX(obj.getDouble(TRANSLATE_X));
        node.setTranslateY(obj.getDouble(TRANSLATE_Y));
        node.setRotate(obj.getDouble(ROTATE));
    }

    // 和上面的函数一样
    static private void setShapeInfo(Shape node, JSONObject obj) {
        node.setFill(Color.valueOf(obj.getString(BG_COLOR)));
        node.setStroke(Color.valueOf(obj.getString(FG_COLOR)));
        node.setStrokeWidth(obj.getDouble(LINE_WIDTH));
        double gaps = obj.getDouble(LINE_GAPS);
        if (gaps > 0) {
            node.getStrokeDashArray().add(gaps);
        }
    }

    // 读取文件时，将一个 JSONObject 对象转换成要显示在界面的控件
    private static Node nodeFromJSON(JSONObject obj) {
        String type = obj.getString(TYPE);
        switch (type) {
            case ELLIPSE:
                Ellipse ellipse = new Ellipse();
                setNodeInfo(ellipse, obj);
                setShapeInfo(ellipse, obj);
                ellipse.setCenterX(obj.getDouble(CENTER_X));
                ellipse.setCenterY(obj.getDouble(CENTER_Y));
                ellipse.setRadiusX(obj.getDouble(RADIUS_X));
                ellipse.setRadiusY(obj.getDouble(RADIUS_Y));
                return ellipse;
            case RECTANGLE:
                Rectangle rectangle = new Rectangle();
                setNodeInfo(rectangle, obj);
                setShapeInfo(rectangle, obj);
                rectangle.setWidth(obj.getDouble(WIDTH));
                rectangle.setHeight(obj.getDouble(HEIGHT));
                return rectangle;
            case LINE:
                Line line = new Line();
                setNodeInfo(line, obj);
                setShapeInfo(line, obj);
                line.setEndX(obj.getDouble(END_X));
                line.setEndY(obj.getDouble(END_Y));
                return line;
            case POLYLINE:
                Polyline polyline = new Polyline();
                setNodeInfo(polyline, obj);
                setShapeInfo(polyline, obj);
                JSONArray points = obj.getJSONArray(POINTS);
                for (int i = 0; i < points.size(); i++) {
                    polyline.getPoints().add(points.getDouble(i));
                }
                return polyline;
            case LABEL:
                Label label = new Label();
                setNodeInfo(label, obj);
                label.setText(obj.getString(TEXT));
                label.setTextFill(Color.valueOf(obj.getString(TEXT_COLOR)));
                label.setFont(new Font(obj.getDouble(TEXT_SIZE)));
                label.setPrefWidth(obj.getDouble(WIDTH));
                label.setPrefHeight(obj.getDouble(HEIGHT));
                label.setWrapText(true);
                String align = obj.getString(TEXT_ALIGNMENT);
                if (align.equals(ALIGN_LEFT)) {
                    label.setAlignment(Pos.TOP_LEFT);
                    label.setTextAlignment(TextAlignment.LEFT);
                } else if (align.equals(ALIGN_CENTER)) {
                    label.setAlignment(Pos.TOP_CENTER);
                    label.setTextAlignment(TextAlignment.CENTER);
                } else {
                    label.setAlignment(Pos.TOP_RIGHT);
                    label.setTextAlignment(TextAlignment.RIGHT);
                }
                return label;
        }

        return null;
    }

    // 将界面的所有元素转换成 JSON 对象后，写入文件
    public static void write(String path, Vector<Vector<Node>> objs) throws IOException {
        JSONArray pages = new JSONArray();
        for (Vector<Node> arr: objs) {
            JSONArray page = new JSONArray();
            pages.add(page);
            for (Node n: arr) {
                page.add(nodeToJSON(n));
            }
        }

        Files.writeString(FileSystems.getDefault().getPath(path),
                JSON.toJSONString(pages, SerializerFeature.PrettyFormat));
    }

    // 读取文本，返回读取到的元素列表
    public static Vector<Vector<Node>> read(String path) throws IOException {
        Vector<Vector<Node>> objs = new Vector<>();

        String str =  Files.readString(FileSystems.getDefault().getPath(path));
        JSONArray pages = JSON.parseArray(str);
        for (Object p: pages) {
            Vector<Node> page = new Vector<>();
            objs.add(page);
            for (Object n: (JSONArray) p) {
                Node node = nodeFromJSON((JSONObject) n);
                if (node != null) {
                    page.add(node);
                }
            }
        }

        return objs;
    }
}
